/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 play_combat_animation.cpp

	$Header: /heroes4/play_combat_animation.cpp $

	$NoKeywords: $

 ************************************************************************/
// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "play_combat_animation.h"

#include "combat_actor_action.h"
#include "combat_creature.h"
#include "battlefield.h"
#include "game_time.h"
#include "options.h"

// ------------------------------------------------------
// class to play a combat animation
// ------------------------------------------------------
t_play_combat_animation::t_play_combat_animation( t_combat_creature&		actor, 
												  t_direction			    direction,
												  t_combat_actor_action_id  action,
												  bool                      play_sound )
					   : t_counted_animation( actor.get_battlefield() ), m_creature( &actor ),
						 t_counted_idle_processor( 20 )
{
	m_action = action;
	m_started = false;
	m_reverse_order = false;
	m_key_frame_hit = false;
	// if direction doesn't match, play a single frame of the wait animation.
	if (play_sound)
		actor.play_sound( action );

	t_combat_actor_action_id new_action;

	if (action == k_combat_actor_action_die)
		actor.clear_spell( k_spell_frozen, get_action_message() );
	if ( actor.has_action( k_combat_actor_action_base_frame, direction ) )
		new_action = k_combat_actor_action_base_frame;
	else
		new_action = k_combat_actor_action_wait;
	m_battlefield.set_current_direction_and_action( actor, direction, new_action );

	// fidget and wait always play at normal speed
	if (m_action == k_combat_actor_action_wait 
		|| m_action == k_combat_actor_action_fidget)
		set_delay( 1000 / actor.get_frames_per_second( action ) );
	else
		set_delay( 100000 / (actor.get_frames_per_second( action ) * get_combat_animation_speed()) );

	// if this is the wait animation, and the wait has no real animation, stop.
	if (action == k_combat_actor_action_wait || action == k_combat_actor_action_fidget)
		animation_ended();
	if (action == k_combat_actor_action_wait && !actor.has_action( action, direction ))
	{
		suspend_idle_processing();
		return;
	}
}

t_play_combat_animation::~t_play_combat_animation()
{
}

void t_play_combat_animation::set_reverse( bool arg )
{
	m_reverse_order = arg;
	if (m_creature->get_current_action() == m_action && arg)
		m_battlefield.set_current_frame_num( *m_creature, m_creature->get_frame_count() - 1 );
}


// ------------------------------------------------------
// class to play a combat animation
// ------------------------------------------------------
void t_play_combat_animation::on_idle()
{
	t_uint32		new_time = get_time();
	int				frame;
	int				frame_count;
	bool			finished;
	t_uint32		next_time = get_next_time() - get_delay();
	t_battlefield&	battlefield = m_battlefield;

	if (m_creature->is_active( k_spell_frozen ))
	{
		t_idle_ptr reference = this;

		suspend_idle_processing();
		animation_ended();
		m_creature->set_wait_animation();
		m_key_frame_handler( *m_creature ); 
		m_end_handler( *m_creature );
		return;
	}

	while (elapsed_time( new_time, next_time ) >= 0)
	{
		next_time += get_delay();
		set_next_time( next_time );

		// check if we are performing the action.
		if (m_creature->get_current_action() != m_action
			&& m_action != k_combat_actor_action_wait )
		{ 
			// if not, and we have started, stop.
			if (m_started)
			{
				t_idle_ptr ref = this;

				suspend_idle_processing();
				animation_ended();
				m_creature->set_wait_animation();
				m_end_handler( *m_creature );
				return;
			}
			// otherwise, start the action
			m_started = true;
			battlefield.set_current_action( *m_creature, m_action );
			frame_count = m_creature->get_frame_count();
			if (m_reverse_order)
				frame = frame_count - 1;
			else
				frame = 0;
		}
		else
		{
			frame		= m_creature->get_current_frame_num();
			frame_count = m_creature->get_frame_count();
			if (m_reverse_order)
				frame--;
			else
				frame++;
		}
		if (m_reverse_order)
			finished = (frame < 0);
		else
			finished = (frame >= frame_count);

		// if we have finished the action...
		if (finished)
		{
			// if this is not a wait cycle, show a single frame of the wait animation.
			if (m_action != k_combat_actor_action_wait)
			{
				// special case: death animation does NOT end in wait.
				if (m_action == k_combat_actor_action_die && !m_reverse_order)
				{
					t_idle_ptr ref = this;

					m_creature->set_underlay( true );
					suspend_idle_processing();
					animation_ended();
					m_creature->set_animation( 0 );
					m_end_handler( *m_creature );
					return;
				}
				if (m_creature->has_action( k_combat_actor_action_base_frame ))
					battlefield.set_current_action( *m_creature, 
					                                  k_combat_actor_action_base_frame );
				else
					battlefield.set_current_action( *m_creature, 
					                                k_combat_actor_action_wait );
				set_delay( 1000 / m_creature->get_frames_per_second() );
				return;
			}
			// otherwise, restart the animation.
			frame = 0;
			assert( (m_creature->get_current_direction() & 1) == 0);
			if (m_creature->get_current_action() == k_combat_actor_action_base_frame)
			{
				battlefield.set_current_action( *m_creature, 
					                              k_combat_actor_action_wait );
				return;
			}
			if (m_creature->has_action( k_combat_actor_action_base_frame ))
			{
				battlefield.set_current_action( *m_creature,
				                                  k_combat_actor_action_base_frame );
				return;
			}
			if (frame_count == 1) // exit if there's no real animation to the wait.
			{
				suspend_idle_processing();
				return;
			}
		}
		battlefield.set_current_frame_num( *m_creature, frame );
		// check for key frame
		if (!m_key_frame_hit && m_creature->get_current_action() == m_action
			&& (frame >= m_creature->get_key_frame( m_action ) || frame == frame_count - 1))
		{
			t_idle_ptr reference = this;

			m_key_frame_handler( *m_creature ); 
			m_key_frame_handler = t_combat_creature_handler();
			m_key_frame_hit = true;
		}
	}
}


